package game;

import dou.net.socket.InvalidProtocolException;
import dou.scheduled.IntervalTaskManager;
import dou.scheduled.TimerManager;
import game.services.ServicesProtocolManager;
import game.socket.RemoteServerSocket;
import game.socket.ServerSocketThread;
import game.task.TaskManagerName;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.logging.log4j.core.config.ConfigurationSource;
import org.apache.logging.log4j.core.config.Configurator;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;

public class GameServer
{
    private static Logger logger;

    /**
     * 日志配置文件地址
     */
    public static final String LOG4J_PROPERTIES = "log4j2.xml";

    /**
     * 服务程序配置文件地址
     */
    public static final String SERVER_CONFIG = "server.cfg";

    /**
     * 服务器入口方法
     * @param args 接受两个参数，分别指定日志配置文件和服务程序配置文件的地址
     * @throws InterruptedException 程序被中断是抛出该异常
     */
    public static void main(String[] args) throws InterruptedException
    {
        long beginTime = System.currentTimeMillis();
        long beginUseMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

        // 载入配置文件
        String filePath = LOG4J_PROPERTIES;
        if(args.length > 1)
        {
            filePath = args[0];
        }
        try
        {
            File file = new File(filePath);
            BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
            ConfigurationSource source = new ConfigurationSource(in);
            Configurator.initialize(null, source);
            // 获取日志对象
            logger = LogManager.getLogger("GameServer");
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }

        if(args.length <= 1)
        {
            ServerConfig.getInstance().loadConfig(SERVER_CONFIG);
        }
        else
        {
            ServerConfig.getInstance().loadConfig(args[1]);
        }

        logger.info("Game server initialization!");

        // TODO : 需要使用数据库就去掉下方的注释
//        try
//        {
//            MysqlUtils.CreateConnection(ServerConfig.getInstance().getDBConnectionPoolCount());
//        }
//        catch(Exception exception)
//        {
//            logger.error("Database initialization error: ", exception);
//            return;
//        }
//        logger.info("Database initialization finished!");

        // 启动计时管理器
        TimerManager.getInstance().initialize(50);
        TimerManager.getInstance().addTaskManager(TaskManagerName.KeepAlive.getName(), new IntervalTaskManager());
        TimerManager.getInstance().start();

        // 阻塞计数对象，一开始设置计数的总量，在 await 方法开始时进行线程阻塞，在计数的总量为 0 使唤醒线程
        CountDownLatch latch = new CountDownLatch(1);

        // 注册所有协议
        try
        {
            ServicesProtocolManager.initializeProtocols();
        }
        catch (InvalidProtocolException exception)
        {
            logger.error("Services protocol initialize failure: ", exception);
            return;
        }

        // 创建套接字服务对象
        RemoteServerSocket remoteSocketServer = null;
        try
        {
            remoteSocketServer = new RemoteServerSocket();
        }
        catch(Exception exception)
        {
            logger.error("RemoteSocketServer start failure: ", exception);
            return;
        }
        // 为套接字服务对象对象分配一个独立的线程运行
        ServerSocketThread remoteServerSocketThread = new ServerSocketThread(remoteSocketServer, ServerConfig.getInstance().getRemoteHost(), ServerConfig.getInstance().getRemotePort(), latch);
        remoteServerSocketThread.start();

        // 阻塞线程并等待套接字服务对象创建成功
        latch.await();

        long endTime = System.currentTimeMillis();
        long endUseMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
        logger.info("Game server start finished and use time " + (endTime - beginTime) + "'ms and memory " + ((endUseMemory - beginUseMemory) / 1024) + " KB!");

        // 程序退出侦听
        Runtime.getRuntime().addShutdownHook(new ShutdownHookThread());

        // 监听没有被捕获的所有异常
        Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler()
        {
            @Override
            public void uncaughtException(Thread thread, Throwable throwable)
            {
                logger.error("Uncaught exception, thread: " + thread.getName() + ", exception: ", throwable);
            }
        });
    }
}
